////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   animsequence.cpp
//  Version:     v1.00
//  Created:     29/4/2002 by Timur.
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "AnimSequence.h"

#include "EntityNode.h"
#include "CVarNode.h"
#include "ScriptVarNode.h"
#include "AnimCameraNode.h"
#include "SceneNode.h"
#include "StlUtils.h"
#include "MaterialNode.h"
#include "EventNode.h"
#include "LayerNode.h"
#include "CommentNode.h"
#include "AnimPostFXNode.h"
#include "AnimScreenFaderNode.h"
#include "I3DEngine.h"
#include "AnimLightNode.h"

#include "IScriptSystem.h"

//////////////////////////////////////////////////////////////////////////
CAnimSequence::CAnimSequence( IMovieSystem *pMovieSystem, uint32 id )
{
	m_lastGenId = 1;
	m_pMovieSystem = pMovieSystem;
	m_flags = 0;
	m_pParentSequence=NULL;
	m_timeRange.Set( 0,10 );
	m_bPaused = false;
	m_bActive = false;
	m_pGroup = NULL;
	m_pOwner = NULL;
	m_pActiveDirector = NULL;
	m_fixedTimeStep = 0;
	m_precached = false;
	m_id = id;
	m_resetting = false;
}

//////////////////////////////////////////////////////////////////////////
CAnimSequence::~CAnimSequence()
{
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::SetName( const char *name )
{
	const char* name0 = GetFullName();
	m_name = name;
	if (strlen(name0) > 0)
	{
		const char* fullname = GetFullName();
		m_pMovieSystem->OnSequenceRenamed(name0, fullname);
	}

	if (GetOwner())
		GetOwner()->OnModified();
}

//////////////////////////////////////////////////////////////////////////
const char* CAnimSequence::GetFullName() const
{
	m_fullNameHolder = m_name;
	IAnimSequenceGroup *pGroup = m_pGroup;
	while (pGroup 
		&& pGroup->GetParent()) // To skip the name of the root node
	{
		m_fullNameHolder = string(pGroup->GetName()) + "/" + m_fullNameHolder;
		pGroup = pGroup->GetParent();
	}

	return m_fullNameHolder.c_str();
}

//////////////////////////////////////////////////////////////////////////
const char* CAnimSequence::GetName() const
{
	return m_name.c_str();
}

//////////////////////////////////////////////////////////////////////////
IAnimSequenceGroup* CAnimSequence::GetGroup() const
{
	return m_pGroup;
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::SetGroup(IAnimSequenceGroup *pGroup)
{
	assert(pGroup);

	const char* name0 = GetFullName();

	if (m_pGroup)
	{
		bool ok = m_pGroup->RemoveChildSequence(this);
		assert(ok);
	}

	m_pGroup = pGroup;

	if (m_pGroup)
	{
		bool ok = m_pGroup->AddChildSequence(this);
		assert(ok);
	}

	if (strlen(name0) > 0)
	{
		const char* fullname = GetFullName();
		m_pMovieSystem->OnSequenceRenamed(name0, fullname);
	}

	if (GetOwner())
		GetOwner()->OnModified();
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::SetGroupAndName(IAnimSequenceGroup *pGroup, const char *name)
{
	const char* name0 = GetFullName();

	if (m_pGroup)
	{
		bool ok = m_pGroup->RemoveChildSequence(this);
	}

	m_pGroup = pGroup;

	if (m_pGroup)
	{
		bool ok = m_pGroup->AddChildSequence(this);
		assert(ok);
	}

	m_name = name;

	if (strlen(name0) > 0)
	{
		const char* fullname = GetFullName();
		m_pMovieSystem->OnSequenceRenamed(name0, fullname);
	}

	if (GetOwner())
		GetOwner()->OnModified();
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::SetFlags( int flags )
{
	m_flags = flags;
}

//////////////////////////////////////////////////////////////////////////
int CAnimSequence::GetFlags() const
{
	return m_flags;
}

//////////////////////////////////////////////////////////////////////////
int CAnimSequence::GetCutSceneFlags(const bool localFlags) const
{
	int currentFlags = m_flags & (NO_HUD | NO_PLAYER | IS_16TO9 | NO_GAMESOUNDS | NO_ABORT);

	if (m_pParentSequence != NULL)
	{
		if (localFlags == true)
		{
			currentFlags &= ~m_pParentSequence->GetCutSceneFlags();
		}
		else
		{
			currentFlags |= m_pParentSequence->GetCutSceneFlags();
		}
	}

	return currentFlags;
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::SetParentSequence(IAnimSequence *pParentSequence)
{
	m_pParentSequence = pParentSequence;
}

//////////////////////////////////////////////////////////////////////////
const IAnimSequence* CAnimSequence::GetParentSequence() const
{
	return m_pParentSequence;
}

//////////////////////////////////////////////////////////////////////////
int CAnimSequence::GetNodeCount() const
{
	return m_nodes.size();
}

//////////////////////////////////////////////////////////////////////////
IAnimNode* CAnimSequence::GetNode( int index ) const
{
	assert( index >= 0 && index < (int)m_nodes.size() );
	return m_nodes[index];
}

//////////////////////////////////////////////////////////////////////////
bool CAnimSequence::AddNode( IAnimNode *node )
{
	assert( node != 0 );

	// Check if this node already in sequence.
	for (int i = 0; i < (int)m_nodes.size(); i++)
	{
		if (node == m_nodes[i])
		{
			// Fail to add node second time.
			return false;
		}
	}
	
	((CAnimNode*)node)->SetSequence(this);
	node->SetTimeRange( m_timeRange );
	m_nodes.push_back( node );

	if (node->GetId() >= (int)m_lastGenId)
		m_lastGenId = node->GetId()+1;

	if(node->NeedToRender())
		AddNodeNeedToRender(node);

	bool bNewDirectorNode = m_pActiveDirector == NULL && node->GetType() == ANODE_SCENE;
	if (bNewDirectorNode)
		m_pActiveDirector = node;

	return true;
}

//////////////////////////////////////////////////////////////////////////
IAnimNode* CAnimSequence::CreateNodeInternal( EAnimNodeType nodeType,uint32 nNodeId )
{
	CAnimNode *node = NULL;

	switch (nodeType)
	{
	case ANODE_ENTITY:
		node = new CAnimEntityNode();
		break;
	case ANODE_CAMERA:
		node = new CAnimCameraNode();
		break;
	case ANODE_CVAR:
		node = new CAnimCVarNode();
		break;
	case ANODE_SCRIPTVAR:
		node = new CAnimScriptVarNode();
		break;
	case ANODE_SCENE:
		node = new CAnimSceneNode();
		break;
	case ANODE_MATERIAL:
		node = new CAnimMaterialNode();
		break;
	case ANODE_EVENT:
		node = new CAnimEventNode();
		break;
	case ANODE_GROUP:
		node = new CAnimNodeGroup();
		break;
	case  ANODE_LAYER:
		node = new CLayerNode();
		break;
	case ANODE_COMMENT:
		node = new CCommentNode();
		break;
	case ANODE_RADIALBLUR:
	case ANODE_COLORCORRECTION:
	case ANODE_DOF:
	case ANODE_HDRSETUP:
	case ANODE_SHADOWSETUP:
		node = CAnimPostFXNode::CreateNode(nodeType);
		break;
	case ANODE_SCREENFADER:
		node = new CAnimScreenFaderNode();
		break;
	case ANODE_LIGHT:
		node = new CAnimLightNode;
		break;
	}

	if (node)
	{
		if (nNodeId == -1)
		{
			nNodeId = m_lastGenId;
		}
		node->SetId(nNodeId);

		AddNode(node);
	}
	return node;
}

//////////////////////////////////////////////////////////////////////////
IAnimNode* CAnimSequence::CreateNode( EAnimNodeType nodeType )
{
	return CreateNodeInternal(nodeType);
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::RemoveNode( IAnimNode *node )
{
	assert( node != 0 );
	for (int i = 0; i < (int)m_nodes.size();)
	{
		if (node == m_nodes[i])
		{
			m_nodes.erase( m_nodes.begin()+i );

			if(node->NeedToRender())
				RemoveNodeNeedToRender(node);

			continue;
		}
		if (m_nodes[i]->GetParent() == node)
			m_nodes[i]->SetParent(0);
		i++;
	}

	// The removed one was the active director node.
	if (m_pActiveDirector == node)
	{
		// Clear the active one.
		m_pActiveDirector = NULL;
		// If there is another director node, set it as active.
		for (AnimNodes::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
		{
			IAnimNode *pNode = *it;
			if (pNode->GetType() == ANODE_SCENE)
			{
				SetActiveDirector(pNode);
				break;
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::RemoveAll()
{
	stl::free_container(m_nodes);
	stl::free_container(m_events);
	stl::free_container(m_nodesNeedToRender);
	m_pActiveDirector = NULL;
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::Reset( bool bSeekToStart )
{
	if (GetFlags() & LIGHT_ANIMATION_SET)
		return;

	m_resetting = true;

	if (!bSeekToStart)
	{
		for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
		{
			(*it)->Reset();
		}
		m_resetting = false;
		return;
	}

	bool bWasActive = m_bActive;

	if (!bWasActive)
		Activate();
	
	SAnimContext ec;
	ec.bSingleFrame = true;
	ec.bResetting = true;
	ec.sequence = this;
	ec.time = m_timeRange.start;
	Animate( ec );
	
	if (!bWasActive)
		Deactivate();
	else
	{
		for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
		{
			IAnimNode *anode = *it;
			anode->Reset();
		}
	}

	m_resetting = false;
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::ResetHard()
{
	if (GetFlags() & LIGHT_ANIMATION_SET)
		return;

	m_resetting = true;

	bool bWasActive = m_bActive;

	if (!bWasActive)
		Activate();

	SAnimContext ec;
	ec.bSingleFrame = true;
	ec.bResetting = true;
	ec.sequence = this;
	ec.time = m_timeRange.start;
	Animate( ec );

	if (!bWasActive)
		Deactivate();
	else
	{
		for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
		{
			IAnimNode *anode = *it;
			anode->ResetHard();
		}
	}

	m_resetting = false;
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::Pause()
{
	if (GetFlags() & LIGHT_ANIMATION_SET)
		return;

	if (m_bPaused)
		return;
	m_bPaused = true;
	// Detach animation block from all nodes in this sequence.
	for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		IAnimNode *anode = *it;
		anode->Pause();
	}
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::Resume()
{
	if (GetFlags() & LIGHT_ANIMATION_SET)
		return;

	if (!m_bPaused)
		return;
	m_bPaused = false;
	// Detach animation block from all nodes in this sequence.
	for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		IAnimNode *anode = *it;
		anode->Resume();
	}
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::StillUpdate()
{
	if (GetFlags() & LIGHT_ANIMATION_SET)
		return;

	for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		IAnimNode *anode = *it;
		anode->StillUpdate();
	}
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::Animate( SAnimContext &ec )
{
	if (GetFlags() & LIGHT_ANIMATION_SET)
		return;

#if !defined(_RELEASE)
	if(CMovieSystem::m_mov_DebugEvents)
	{
		gEnv->pScriptSystem->SetGlobalValue("CurrentCinematicName",m_name.c_str());
	}
#endif

	ec.sequence = this;
	// Evaluate all animation nodes in sequence.
	// The director first.
	if(m_pActiveDirector)
		m_pActiveDirector->Animate(ec);
	for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		// Make sure correct animation block is binded to node.
		IAnimNode *anode = *it;
		// All other (inactive) director nodes are skipped.
		if(anode->GetType() == ANODE_SCENE)
			continue;
		// If this is a descendant of a director node and that director is currently not active, skip this one.
		IAnimNode *pParentDirector = anode->HasDirectorAsParent();
		if(pParentDirector && pParentDirector != m_pActiveDirector)
			continue;
		// Animate node.
		anode->Animate( ec );
	}
#if !defined(_RELEASE)
	if(CMovieSystem::m_mov_DebugEvents)
	{
		gEnv->pScriptSystem->SetGlobalToNull("CurrentCinematicName");
	}
#endif
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::Render()
{
	for (AnimNodes::iterator it = m_nodesNeedToRender.begin(); it != m_nodesNeedToRender.end(); ++it)
	{
		IAnimNode *anode = *it;
		anode->Render();
	}
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::Activate()
{
	if (m_bActive)
		return;

	m_bActive = true;
	// Assign animation block to all nodes in this sequence.
	for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		IAnimNode *anode = *it;
		((CAnimNode*)anode)->Activate(true);
	}

	// If this sequence is cut scene disable player.
	if (m_flags & CUT_SCENE)
	{
	}

	// Do not precache data during resetting as it immediately gets removed again anyway
	if (!m_precached && !m_resetting)
		PrecacheData();
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::Deactivate()
{
	if (!m_bActive)
		return;

	// Detach animation block from all nodes in this sequence.
	for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		IAnimNode *anode = *it;
		((CAnimNode*)anode)->Activate(false);
		anode->Reset();
	}
	// If this sequence is cut scene, enable player.
	if (m_flags & CUT_SCENE)
	{
	}

	// Remove a possibly cached game hint associated with this anim sequence.
	stack_string sTemp("anim_sequence_");
	sTemp += m_name;
	gEnv->pSoundSystem->RemoveCachedAudioFile(sTemp.c_str(), false);

	m_bActive = false;
	m_precached = false;
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::PrecacheData(float startTime /* = 0.0f */)
{
	if (m_precached)
		return;

	// Try to cache this sequence's game hint if one exists.
	stack_string sTemp("anim_sequence_");
	sTemp += m_name;

	if (gEnv->pSoundSystem)
	{
		// Make sure to use the non-serializable game hint type as trackview sequences get properly reactivated after load
		gEnv->pSoundSystem->CacheAudioFile(sTemp.c_str(), eAFCT_GAME_HINT);
	}

	gEnv->pLog->Log("=== Precaching render data for cutscene: %s ===", GetFullName());
	
	// precache animation keys
	for (AnimNodes::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		IAnimNode *anode = *it;
		if (anode->GetType() == ANODE_ENTITY)
		{
			CAnimEntityNode* entityNode = static_cast<CAnimEntityNode*>(anode);
			entityNode->PrecacheKeys(startTime);

			if(IEntity *pEntity = entityNode->GetEntity())
				if(IEntityRenderProxy *pRenderProxy = (IEntityRenderProxy*)pEntity->GetProxy(ENTITY_PROXY_RENDER))
					if(IRenderNode * pRenderNode = pRenderProxy->GetRenderNode())
						gEnv->p3DEngine->PrecacheRenderNode(pRenderNode, 4.f);
		}	
	}

	m_precached = true;
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::Serialize( XmlNodeRef &xmlNode,bool bLoading, bool bLoadEmptyTracks )
{
	if (bLoading)
	{
		// Load.
		RemoveAll();

		Range timeRange;
		const char *name = xmlNode->getAttr( "Name" );
		xmlNode->getAttr( "Flags",m_flags );
		xmlNode->getAttr( "StartTime",timeRange.start );
		xmlNode->getAttr( "EndTime",timeRange.end );
		xmlNode->getAttr( "ID",m_id );
		
		string path = name;
		size_t index;
		if ((index = path.rfind('/')) != -1)
		{
			string groupName = path.Left(index);
			IAnimSequenceGroup *pGroup = m_pMovieSystem->FindSequenceGroup(groupName.c_str());
			if (pGroup)
			// If there is a group already loaded, just set the relationship.
			{
				SetGroupAndName(pGroup, path.Right(path.length() - index - 1).c_str());
			}
			else
			// Otherwise, set the name to the full path and let the later loading of groups handle the setting of the relationship.
			{
				SetGroupAndName(m_pMovieSystem->GetRootGroup(), name);
			}
		}
		else
		{
			SetGroupAndName(m_pMovieSystem->GetRootGroup(), name);
		}
		// Loading.
		XmlNodeRef nodes = xmlNode->findChild( "Nodes" );
		if (nodes)
		{
			uint32 id;
			int type;
			for (int i = 0; i < nodes->getChildCount(); i++)
			{
				XmlNodeRef xn = nodes->getChild(i);
				xn->getAttr( "Id",id );
				
				if (!xn->getAttr( "Type",type ))
					continue;

				IAnimNode *node = CreateNodeInternal( (EAnimNodeType)type,id );
				if (!node)
					continue;
				
				node->Serialize( xn,bLoading,bLoadEmptyTracks );
			}

			// When all nodes loaded restore group hierarchy
			for (AnimNodes::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
			{
				IAnimNode *anode = *it;
				((CAnimNode*)anode)->PostLoad();
				
				// And properly adjust the 'm_lastGenId' to prevent the id clash.
				if (anode->GetId() >= (int)m_lastGenId)
					m_lastGenId = anode->GetId()+1;
			}
		}
		XmlNodeRef events = xmlNode->findChild( "Events" );
		if (events)
		{
			string eventName;
			for (int i = 0; i < events->getChildCount(); i++)
			{
				XmlNodeRef xn = events->getChild(i);
				eventName = xn->getAttr( "Name" );
				m_events.push_back(eventName);

				// Added
				for (TTrackEventListeners::iterator j = m_listeners.begin(); j != m_listeners.end(); ++j)
					(*j)->OnTrackEvent(this, ITrackEventListener::TER_ADDED, eventName, NULL);
			}
		}
		// Setting the time range must be done after the loading of all nodes
		// since it sets the time range of tracks, also.
		SetTimeRange( timeRange );
		Deactivate();
		//ComputeTimeRange();

		if (GetOwner())
			GetOwner()->OnModified();
	}
	else
	{
		const char* fullname = GetFullName();
		xmlNode->setAttr( "Name",fullname );	// Save the full path as a name.
		xmlNode->setAttr( "Flags",m_flags );
		xmlNode->setAttr( "StartTime",m_timeRange.start );
		xmlNode->setAttr( "EndTime",m_timeRange.end );
		xmlNode->setAttr( "ID",m_id );

		// Save.
		XmlNodeRef nodes = xmlNode->newChild( "Nodes" );
		int num = GetNodeCount();
		for (int i = 0; i < num; i++)
		{
			IAnimNode *node = GetNode(i);
			if (!node)
				continue;
			XmlNodeRef xn = nodes->newChild( "Node" );
			node->Serialize( xn,bLoading,true );
		}

		XmlNodeRef events = xmlNode->newChild( "Events" );
		TrackEvents::iterator event = m_events.begin();
		TrackEvents::iterator eventEnd = m_events.end();
		for (; event != eventEnd; ++event)
		{
			XmlNodeRef xn = events->newChild( "Event" );
			xn->setAttr( "Name", *event );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::SetTimeRange( Range timeRange )
{
	m_timeRange = timeRange;
	// Set this time range for every track in animation.
		// Set time range to be in range of largest animation track.
	for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		IAnimNode *anode = *it;
		anode->SetTimeRange( timeRange );
	}
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::AdjustKeysToTimeRange( const Range &timeRange )
{
	float offset = timeRange.start - m_timeRange.start;
	// Calculate scale ratio.
	float scale = timeRange.Length() / m_timeRange.Length();
	m_timeRange = timeRange;

	// Set time range to be in range of largest animation track.
	for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		IAnimNode *anode = *it;

		int trackCount = anode->GetTrackCount();
		for (int paramIndex = 0; paramIndex < trackCount; paramIndex++)
		{
			IAnimTrack *pTrack = anode->GetTrackByIndex(paramIndex);
			int nkey = pTrack->GetNumKeys();
			for (int k = 0; k < nkey; k++)
			{
				float keytime = pTrack->GetKeyTime(k);
				keytime = offset + keytime*scale;
				pTrack->SetKeyTime(k,keytime);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::ComputeTimeRange()
{
	Range timeRange;
	//timeRange.start = FLT_MAX;
	//timeRange.end = FLT_MIN;

	timeRange = m_timeRange;
	
	// Set time range to be in range of largest animation track.
	for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		IAnimNode *anode = *it;
		
		int trackCount = anode->GetTrackCount();
		for (int paramIndex = 0; paramIndex < trackCount; paramIndex++)
		{
			IAnimTrack *pTrack = anode->GetTrackByIndex(paramIndex);
			int nkey = pTrack->GetNumKeys();
			if (nkey > 0)
			{
				timeRange.start = std::min( timeRange.start,pTrack->GetKeyTime(0) );
				timeRange.end = std::max( timeRange.end,pTrack->GetKeyTime(nkey-1) );
			}
		}
	}

	if (timeRange.start > 0)
		timeRange.start = 0;

	m_timeRange = timeRange;
}

bool CAnimSequence::AddTrackEvent(const char* szEvent)
{
	CRY_ASSERT(szEvent && szEvent[0]);
	if (stl::push_back_unique(m_events, szEvent))
	{
		NotifyTrackEvent(ITrackEventListener::TER_ADDED, szEvent);
		return true;
	}
	else
		return false;
}

bool CAnimSequence::RemoveTrackEvent(const char* szEvent)
{
	CRY_ASSERT(szEvent && szEvent[0]);
	if (stl::find_and_erase(m_events, szEvent))
	{
		NotifyTrackEvent(ITrackEventListener::TER_REMOVED, szEvent);
		return true;
	}
	else
		return false;
}

bool CAnimSequence::RenameTrackEvent(const char* szEvent, const char* szNewEvent)
{
	CRY_ASSERT(szEvent && szEvent[0]);
	CRY_ASSERT(szNewEvent && szNewEvent[0]);

	for (size_t i=0; i<m_events.size(); ++i)
	{
		if (m_events[i] == szEvent)
		{
			m_events[i] = szNewEvent;
			NotifyTrackEvent(ITrackEventListener::TER_RENAMED, szEvent, szNewEvent);
			return true;
		}
	}
	return false;
}

bool CAnimSequence::MoveUpTrackEvent(const char* szEvent)
{
	CRY_ASSERT(szEvent && szEvent[0]);

	for (size_t i=0; i<m_events.size(); ++i)
	{
		if (m_events[i] == szEvent)
		{
			assert(i > 0);
			if (i > 0)
			{
				std::swap(m_events[i-1], m_events[i]);
				NotifyTrackEvent(ITrackEventListener::TER_MOVED_UP, szEvent);
			}
			return true;
		}
	}

	return false;
}

bool CAnimSequence::MoveDownTrackEvent(const char* szEvent)
{
	CRY_ASSERT(szEvent && szEvent[0]);

	for (size_t i=0; i<m_events.size(); ++i)
	{
		if (m_events[i] == szEvent)
		{
			assert(i < m_events.size() - 1);
			if (i < m_events.size() - 1)
			{
				std::swap(m_events[i], m_events[i+1]);
				NotifyTrackEvent(ITrackEventListener::TER_MOVED_DOWN, szEvent);
			}
			return true;
		}
	}

	return false;
}

void CAnimSequence::ClearTrackEvents()
{
	m_events.clear();
}

int CAnimSequence::GetTrackEventsCount() const
{
	return (int)m_events.size();
}

char const* CAnimSequence::GetTrackEvent(int iIndex) const
{
	char const* szResult = NULL;
	const bool bValid = (iIndex >= 0 && iIndex < GetTrackEventsCount());
	CRY_ASSERT(bValid);

	if (bValid)
		szResult = m_events[iIndex];

	return szResult;
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::NotifyTrackEvent(ITrackEventListener::EReason reason, 
											const char* event, const char* param)
{
	// Notify listeners
	for (TTrackEventListeners::iterator j = m_listeners.begin(); j != m_listeners.end(); ++j)
		(*j)->OnTrackEvent(this, reason, event, (void*)param);
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::TriggerTrackEvent(const char* event, const char* param)
{
	NotifyTrackEvent(ITrackEventListener::TER_TRIGGERED, event, param);
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::AddTrackEventListener(ITrackEventListener *pListener)
{
	if (std::find(m_listeners.begin(),m_listeners.end(),pListener) == m_listeners.end())
	{
		m_listeners.push_back(pListener);
	}
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::RemoveTrackEventListener(ITrackEventListener *pListener)
{
	TTrackEventListeners::iterator it = std::find(m_listeners.begin(),m_listeners.end(),pListener);
	if (it != m_listeners.end())
	{
		m_listeners.erase(it);
	}
}

//////////////////////////////////////////////////////////////////////////
IAnimNode* CAnimSequence::FindNodeById( int nNodeId )
{
	for (AnimNodes::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		IAnimNode *anode = *it;
		if (((CAnimNode*)anode)->GetIDFast() == nNodeId)
		{
			return anode;
		}
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
IAnimNode* CAnimSequence::FindNodeByName( const char *sNodeName )
{
	for (AnimNodes::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		IAnimNode *anode = *it;
		// Case insesetive name comparasion.
		if (stricmp(((CAnimNode*)anode)->GetNameFast(),sNodeName) == 0)
		{
			return anode;
		}
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
void CAnimSequence::ReorderNode( IAnimNode *node,IAnimNode *pPivotNode, bool next )
{
	if (node == pPivotNode || !node)
		return;

	_smart_ptr<IAnimNode> pTempHolder = node; // Keep reference to node so it is not deleted by erasing from list.
	stl::find_and_erase( m_nodes,node );

	AnimNodes::iterator it;
	for (it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		IAnimNode *anode = *it;
		if (anode == pPivotNode)
		{
			if (next)
				m_nodes.insert( it+1,node );
			else
				m_nodes.insert( it, node );
			break;
		}
	}
	if (it == m_nodes.end())
	{
		m_nodes.insert( m_nodes.begin(),node );
	}
}

void CAnimSequence::CopyNodeChildren(XmlNodeRef& xmlNode, IAnimNode* pAnimNode)
{
	for (int k=0; k < GetNodeCount(); ++k)
	{
		if(GetNode(k)->GetParent() == pAnimNode)
		{
			XmlNodeRef childNode = xmlNode->newChild( "Node" );
			GetNode(k)->Serialize( childNode,false,true );
			if (GetNode(k)->GetType() == ANODE_GROUP
				|| pAnimNode->GetType() == ANODE_SCENE)
				CopyNodeChildren(xmlNode, GetNode(k));
		}
	}
}

void CAnimSequence::CopyNodes(XmlNodeRef& xmlNode, IAnimNode** pSelectedNodes, uint32 count)
{
	for (uint32 i = 0; i < count; ++i)
	{
		IAnimNode *pAnimNode = pSelectedNodes[i];
		if (pAnimNode)
		{
			XmlNodeRef xn = xmlNode->newChild( "Node" );
			pAnimNode->Serialize( xn,false,true );
			// If it is a group node, copy its children also.
			if (pAnimNode->GetType() == ANODE_GROUP
				|| pAnimNode->GetType() == ANODE_SCENE)
				CopyNodeChildren(xmlNode, pAnimNode);
		}
	}
}

void CAnimSequence::PasteNodes(const XmlNodeRef& xmlNode, IAnimNode *pParent)
{
	int type, id;
	std::map<int, IAnimNode*> idToNode;
	for (int i = 0; i < xmlNode->getChildCount(); i++)
	{
		XmlNodeRef xn = xmlNode->getChild(i);

		if (!xn->getAttr( "Type",type ))
			continue;

		xn->getAttr( "Id",id );

		IAnimNode *node = CreateNode((EAnimNodeType)type);
		if (!node)
			continue;

		idToNode[id] = node;

		int newId = static_cast<CAnimNode*>(node)->GetId();
		node->Serialize( xn,true,true );
		static_cast<CAnimNode*>(node)->SetId(newId);

		int parentId = 0;
		if(xn->getAttr( "ParentNode",parentId ))
			node->SetParent(idToNode[parentId]);
		else
		// This means a top-level node.
		{
			if(pParent)
				node->SetParent(pParent);
		}
	}
}

bool CAnimSequence::AddNodeNeedToRender(IAnimNode* pNode)
{
	assert( pNode != 0 );
	return stl::push_back_unique(m_nodesNeedToRender, pNode);
}

void CAnimSequence::RemoveNodeNeedToRender(IAnimNode* pNode)
{
	assert( pNode != 0 );
	stl::find_and_erase(m_nodesNeedToRender, pNode);
}

void CAnimSequence::SetActiveDirector(IAnimNode *pDirectorNode)
{
	assert(pDirectorNode->GetType() == ANODE_SCENE);
	if(pDirectorNode->GetType() != ANODE_SCENE)
		return;		// It's not a director node.

	if(pDirectorNode->GetSequence() != this)
		return;		// It's not a node belong to this sequence.

	m_pActiveDirector = pDirectorNode;
}

IAnimNode *CAnimSequence::GetActiveDirector() const
{
	return m_pActiveDirector;
}

bool CAnimSequence::IsAncestorOf(const IAnimSequence *pSequence) const
{
	assert(this != pSequence);
	if (this == pSequence)
		return true;

	for (AnimNodes::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
	{
		IAnimNode *pNode = *it;
		if (pNode->GetType() == ANODE_SCENE)
		{
			IAnimTrack *pSequenceTrack = pNode->GetTrackForParameter(APARAM_SEQUENCE);
			if (pSequenceTrack)
			{
				for (int i=0; i<pSequenceTrack->GetNumKeys(); ++i)
				{
					ISequenceKey key;
					pSequenceTrack->GetKey(i, &key);
					if (stricmp(key.szSelection, pSequence->GetFullName()) == 0)
						return true;
					IAnimSequence *pChild = GetMovieSystem()->FindSequence(key.szSelection);
					if (pChild && pChild->IsAncestorOf(pSequence))
						return true;
				}
			}
		}
	}

	return false;
}

#include UNIQUE_VIRTUAL_WRAPPER(IAnimSequence)
